feat: git history in lecture headers (PLAN Phase 1)#83
Conversation
Reproduce the quantecon-book-theme page header: a "Last changed: <date>" control that expands a changelog dropdown of recent commits, with GitHub-linked hashes and a full-history link. Build side: mystmd has no built-in last-modified support (upstream request jupyter-book/mystmd#2213 is open), and plugin transforms cannot modify page frontmatter (it is extracted before document-stage transforms run), so plugins/git-metadata.mjs attaches `git log --follow` results to the page AST as mdast.data.git_metadata, which flows into the page JSON. Per-page `site.git_metadata` frontmatter acts as a manual override. Theme side: PageHeaderHistory.tsx (Radix popover; Esc/ARIA; dark mode; relative times computed at render and gated behind mount so statically exported HTML hydrates cleanly). Commit links keep the `.myst` suffix — they target the source repo, unlike LaunchButton's notebook URLs; the full-history link derives from the mystmd-computed source_url. Tests: node-level plugin e2e (npm run test:plugin, wired into the visual CI job) builds a throwaway project with the real myst CLI; the visual fixture pins deterministic git_metadata on features.md and a new history-open snapshot covers the open dropdown under a frozen clock. -darwin baselines refreshed; -linux to follow via /update-snapshots. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/update-snapshots |
|
🎭 Refreshed visual baselines in e007bf7:
|
🎭 Visual regression resultsDetails
Skipped testsmobile-chrome › theme.spec.ts › QuantEcon theme — visual regression › launch-colab |
There was a problem hiding this comment.
Pull request overview
This PR adds build-time Git metadata extraction and a theme UI control to show “Last changed: ” with an expandable per-page changelog in the page header, mirroring quantecon-book-theme.
Changes:
- Add a MyST
document-stage transform plugin (plugins/git-metadata.mjs) that injects per-page git history intomdast.data.git_metadata, plus a node-level e2e test. - Add a new header component (
PageHeaderHistory) rendered inProjectFrontmatterto display last-modified + a Radix popover changelog with commit/history links. - Extend CI + visual fixtures/snapshots and update docs (README/PLAN/CHANGELOG) to describe usage and deterministic testing.
Reviewed changes
Copilot reviewed 13 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/visual/theme.spec.ts | Adds a visual test covering opening/closing the history popover and verifying link targets/timestamps. |
| tests/visual/fixture/myst.yml.in | Updates fixture repo URL to the correct GitHub repository for link generation. |
| tests/visual/fixture/features.md | Pins deterministic site.git_metadata so visual snapshots don’t churn with real git timestamps. |
| tests/plugin/git-metadata.test.mjs | Adds e2e test that runs the real myst CLI and asserts mdast.data.git_metadata injection behavior. |
| README.md | Documents the new git-metadata plugin and header behavior, including configuration and overrides. |
| plugins/git-metadata.mjs | Implements the MyST transform plugin that runs git log --follow and attaches results to the page AST. |
| PLAN.md | Marks Phase 1 tasks as completed and records findings/implementation notes. |
| package.json | Adds test:plugin script to run node’s test runner for plugin e2e. |
| CHANGELOG.md | Records the new “Git history in page headers” feature in Unreleased. |
| app/types.ts | Adds git_metadata to TemplateOptions for the page-level override channel. |
| app/components/ProjectFrontmatter.tsx | Renders PageHeaderHistory in the bordered header block. |
| app/components/PageHeaderHistory.tsx | New Radix popover UI for last-changed + changelog, reading override first then injected AST data. |
| .github/workflows/ci.yml | Runs the new plugin e2e test in the visual job after building the theme template. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… is empty Address two Copilot review points on #83: only rewrite source_url into a commits view when it actually has the /blob/ shape (otherwise render no full-history link), and render the last-changed line as plain text when there are no changelog entries instead of a non-functional popover trigger. No pixel change — the fixture exercises the populated path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed Copilot's 3 comments: 2 fixed in d68317a (full-history link shape guard; plain text instead of a dead popover trigger when the changelog is empty — no pixel change, all 10 visual tests + plugin e2e green), 1 push-back with verification (clock.setFixedTime works standalone without clock.install()). |
…ing) (#84) * ci: per-PR rendered previews on GitHub Pages Add preview.yml: on each same-repo PR, build the theme, statically build the real QuantEcon/lecture-python-programming lectures with it (myst build --html, BASE_URL set to the Pages subpath), and deploy to the gh-pages branch under pr-preview/pr-<n>/ via rossjrw/pr-preview-action (sticky link comment, teardown on close). Self-contained on GITHUB_TOKEN - the org-secrets blocker that deferred the Netlify approach is gone. The content repo is a legacy Jupyter Book, so the build pipes a confirmation into `myst init` to run the JB->MyST upgrade at build time - the preview doubles as a migration-readiness check. Content is cloned at full depth so the git-metadata plugin (PR #83) renders real per-page history once it lands; a guard skips plugin injection on branches that predate it. Static export is the production path the Playwright harness (live `myst start`) never exercises; the preview is qualitative and non-gating - Playwright remains the only gate. Validated end-to-end locally: JB upgrade under non-TTY stdin, yq config patching, 4.6s/45MB build, BASE_URL-prefixed assets, SSR'd history header on real git data. gh-pages was seeded with a root .nojekyll (Pages would otherwise drop the underscore-prefixed build/_assets/ paths) and a landing index.html; Pages serves from the gh-pages branch root. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * ci: bump preview.yml actions to Node 24 runtimes Same checkout/setup-node v4 -> v5 bump as #85 (lowest node24 majors, pure runtime ports). rossjrw/pr-preview-action@v1 stays: its internal JamesIves/marocchino pins are upstream's to update and run under the forced node24 runtime from 2026-06-16. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…eleased] Added entries
|
|
@DrDrij @jstac I am currently migrating our features from quantecon-book-theme to the mystmd theme. This one is implementing the git history. I quite like the style we have on: https://python-programming.quantecon.org/python_by_example.html with a drop down integrated into the header bar. Here is the first pass implementation -- it is an interesting option so I thought I would see what you thought before iterating to match. https://quantecon.github.io/quantecon-theme.mystmd/pr-preview/pr-83/python-by-example/ It presents a window instead. 🤔 Note: don't worry about the broken html below the preview :-) |
…ab-first note + git-history section
|
Looks great to me @mmcky I would shrink the size of the qe logo in the right hand menu somewhat, since it's quite bright and maybe distracting for readers, and perhaps lift it up to the top of that menu (again to make it less distracting). |
|
+1 to shifting inline with names. thanks @DrDrij |
Acts on the team's review of #83 (jstac, DrDrij): - Move the "Last changed: <date>" control onto the author row, aligned right (more semantic, saves a header line); it wraps below authors on narrow viewports and renders nothing on pages without git metadata. - Replace the anchored Radix popover with a centred Radix Dialog modal so the changelog clears the left/right page menus and centres on mobile. Adds a QuantEcon-blue border + dim backdrop so it stands out in Chrome (Safari already drew its own outline) and a close (X) button alongside Esc / backdrop-click. Fixture sets authors on features.md so the snapshots exercise the new authors-left / last-changed-right layout. -darwin baselines refreshed (features + history-open, desktop + mobile); -linux baselines need a /update-snapshots comment on the PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
/update-snapshots |
|
Pushed the first iteration acting on the review (2b5b3fe):
The QE-logo tweak (@jstac) is a right-hand-sidebar/theme item rather than part of this changelog control, so I've split it out into #96. @jstac @DrDrij — the open question before I polish further: do you prefer this centred modal, or a slide-down panel that drops out of the header inline (closer to the current book-theme dropdown on python-programming that I linked)? Trade-offs as I see them: the modal is unambiguous on mobile and can't collide with the side menus, but it's a heavier "window" interaction; the slide-down keeps things lighter and anchored to the control, but needs care so it doesn't conflict with the lhs/rhs menus (the thing that pushed us to a modal in the first place). Happy to prototype the slide-down as well so we can compare side by side. The rendered preview will refresh once the gh-pages deploy for this push completes: https://quantecon.github.io/quantecon-theme.mystmd/pr-preview/pr-83/ |
|
🎭 Refreshed visual baselines in 8c0229f:
|
… trigger CI) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@mmcky would you mind showing me a side-by-side comparison of the feature you want input on? |
|
@jstac this is our (a) CURRENT on quantecon-bok-theme vs (b) this
(a) slides down on the page and collapses back up Note: (a) is from quantecon-book-theme so we would adapt it for this mystmd theme. So mainly thinking through mechanism. |
|
My vote is for (a) but I like both. |
|
@DrDrij any thoughts on this one? |



Summary
Phase 1 of PLAN.md — reproduces the
quantecon-book-themepage header: a "Last changed: ⟨date⟩" control that expands into a changelog dropdown of the last N commits, with GitHub-linked commit hashes and a "full history" link.Research findings (gating question from PLAN)
document-stage transforms run (myst-cli/src/process/mdast.ts), and myst-cli passes no options to transform plugins. The page AST does flow into the page JSON intact, so the plugin attachesmdast.data.git_metadatathere (verified end-to-end against both the QuantEcon mystmd fork and the standard plugin API).site:frontmatter passes through (validated as a plain object, deferred to the site template) — used as the manual override and as the deterministic data source for visual snapshots.Implementation
plugins/git-metadata.mjs—document-stage transform;git log --followper source file (5s timeout); silent no-op for untracked files / non-git checkouts / missing git;QE_GIT_METADATA_MAXcaps entries (default 10). Single self-contained.mjs, copyable into lecture repos — the sharedquantecon-myst-pluginshome question stays open (noted in PLAN.md).app/components/PageHeaderHistory.tsx— Radix popover (Esc-close, ARIA, dark mode, QuantEcon blue); readssite.git_metadataoverride first, then the injected AST data; renders nothing when neither exists. Relative times are computed at render and gated behind mount so statically exported HTML hydrates cleanly. Commit links keep the.mystsuffix (they target the source repo, unlike LaunchButton's notebook URLs); the full-history link derives from the mystmd-computedsource_url.ProjectFrontmatter.tsxinside the bordered header block.Tests
npm run test:plugin(new, wired into thevisualCI job): node-level e2e that builds a throwaway MyST project with the realmystCLI and asserts the injected AST data — changelog ordering, pipe-in-subject handling, ISO dates, untracked-page no-op. Passes locally in ~1s.features.mdpins deterministicgit_metadata; newhistory-opentest freezes the clock (page.clock.setFixedTime) and asserts button text, commit/full-history hrefs, relative times, Esc-close, plus a viewport snapshot. All 10 desktop+mobile tests and the FOUC guard pass locally.-darwinbaselines refreshed (features + new history-open).-linuxbaselines need a/update-snapshotscomment on this PR — thevisualjob will fail until that runs.quantecon-theme-srcGitHub URL in the fixture'smyst.yml.in.Notes for review
project.plugins— see the new README section.🤖 Generated with Claude Code